package com.gitee.wsl.ext.string

import kotlin.math.min

fun String.trimLineStart() = this.splitToSequence("\n").map { it.trim() }.joinToString("\n")

/**
 * Returns the maximal line length of a multiline string.
 *
 * The string is first split by the [separator] and then the max length is computed
 */
fun String.maxLineLength(separator: String = "\n"): Int =
    split(separator).maxOfOrNull { it.length } ?: 0

/**
 * 手动换行
 * */
fun String.breakLine(length: Int): String {
    val step = if (length <= 0) {
        15
    } else {
        length
    }

    if (this.isBlank()) {
        return this
    }

    val lines = this.length / step

    if (this.length <= step) {
        return this
    } else {
        if (this.length % step == 0) {
            //整除
            val builder = StringBuilder()
            for (i in 0 until lines) {
                if (i == lines - 1) {
                    //最后一段文字
                    builder.append(this.substring(i * step))
                } else {
                    val s = this.substring(i * step, (i + 1) * step)
                    builder.append(s).append("\r\n")
                }
            }
            return builder.toString()
        } else {
            val builder = StringBuilder()
            for (i in 0..lines) {
                if (i == lines) {
                    //最后一段文字
                    builder.append(this.substring(i * step))
                } else {
                    val s = this.substring(i * step, (i + 1) * step)
                    builder.append(s).append("\r\n")
                }
            }
            return builder.toString()
        }
    }
}

fun String.withNewLinesStripped() = split("\r\n")
    .first()
    .split("\n")
    .first()

/**
 * Return a copy of the supplied string with commas added every three digits.
 * The substring indicated by the supplied range is assumed to contain only
 * a whole number, with no decimal point.
 * Inserting a digit separator every 3 digits appears to be
 * at least somewhat acceptable, though not necessarily preferred, everywhere.
 * The grouping separator in the result is NOT localized.
 */
fun String.addCommas(begin: Int, end: Int): String {
    // Resist the temptation to use Java's NumberFormat, which converts to long or double
    // and hence doesn't handle very large numbers.
    val result = StringBuilder()
    var current = begin
    while (current < end && (this[current] == '-' || this[current] == ' ')) {
        ++current
    }
    result.append(this, begin, current)
    while (current < end) {
        result.append(this[current])
        ++current
        if ((end - current) % 3 == 0 && end != current) {
            result.append(',')
        }
    }
    return result.toString()
}

/**
 * Returns the longest string `prefix` such that `a.toString().startsWith(prefix) &&
 * b.toString().startsWith(prefix)`, taking care not to split surrogate pairs. If `a` and
 * `b` have no common prefix, returns the empty string.
 *
 * @since 11.0
 */
fun String.Companion.commonPrefix(a: CharSequence, b: CharSequence): String {
    val maxPrefixLength = min(a.length, b.length)
    var p = 0
    while (p < maxPrefixLength && a[p] == b[p]) {
        p++
    }
    if (validSurrogatePairAt(a, p - 1) || validSurrogatePairAt(b, p - 1)) {
        p--
    }
    return a.subSequence(0, p).toString()
}

/**
 * Returns the longest string `suffix` such that `a.toString().endsWith(suffix) &&
 * b.toString().endsWith(suffix)`, taking care not to split surrogate pairs. If `a` and
 * `b` have no common suffix, returns the empty string.
 *
 * @since 11.0
 */
fun String.Companion.commonSuffix(a: CharSequence, b: CharSequence): String {
    val maxSuffixLength = min(a.length, b.length)
    var s = 0
    while (s < maxSuffixLength && a[a.length - s - 1] == b[b.length - s - 1]) {
        s++
    }
    if (validSurrogatePairAt(a, a.length - s - 1) || validSurrogatePairAt(b, b.length - s - 1)
    ) {
        s--
    }
    return a.subSequence(a.length - s, a.length).toString()
}


private fun validSurrogatePairAt(string: CharSequence, index: Int): Boolean {

    return (index >= 0 && index <= string.length - 2
            && string[index].isHighSurrogate()
            && string[index + 1].isLowSurrogate())
}

// 处理s的尾字折行，返回折行完成后首行的字符数
fun String.Companion.cutLine(s: String, c: Char): Int {
    // edited by bd, 2018.4.4, 这里的换行规则比较旧了，对于判定为"一个单词"的连续英文字符数字组合，不用考虑是否避尾
    // 如果当前折行长度len已知，那就不去计算，否则计算当前折行时首行字符数
    var c = c
    val len = s.length - 1

    // 如果尾字符c已知，那就不用去计算，否则计算尾字符
    if (c.code == 0) {
        c = s[len]

        // 在需要在字符c前断行时，首先要考虑c是否是避首字符
        // edited by bdl, 2011.5.17, 当避首字符也是英文字符时（其实就是小数点）
    }
    val canBeHead = canBeHead(c)
    val isEnglishChar = isEnglishChar(c)
    if (!canBeHead && isEnglishChar) {
        // 由于连续的英文字符需要视为一个单词共同换行，所以需要判断是否本行全部为连续英文字符
        var seek = len - 1
        var loc = 0
        var hasHead = canBeHead(c)
        var letterbreak = false
        while (seek >= 0 && loc == 0) {
            val seekChar = s[seek]
            if (!isEnglishChar(seekChar)) {
                letterbreak = true
                if (!hasHead) {
                    if (canBeHead(seekChar)) {
                        // 如果本行中出现非避首字符（排除首字），那么设定有首字为true
                        hasHead = true
                    }
                    seek--
                } else {
                    // 如果本行中出现非英文字符，那么判断该字符是否是避尾字符
                    if (canBeFoot(seekChar)) {
                        // 如果是非避尾字符，那么从这个字符后折行即可
                        loc = seek + 1
                    } else {
                        hasHead = if (canBeHead(seekChar)) {
                            true
                        } else {
                            false
                        }
                        seek--
                    }
                }
            } else if (letterbreak) {
                // 如果出现避尾字符之后，又找到英文字符，那么从该英文字符后断开就好
                loc = seek + 1
            } else {
                hasHead = if (canBeHead(seekChar)) {
                    true
                } else {
                    false
                }
                seek--
            }
        }
        return if (loc > 0) {
            // 如果本行中有非英文字符
            loc
        } else {
            // 如果本行中全都是英文字符或者是连续的避尾字符，那么正常切分
            len
        }
    } else if (!canBeHead) {
        // c是避首字符
        var seek = len - 1
        var loc = 0
        var hasHead = false
        // 找到第一个非避首字符
        while (seek >= 0 && loc == 0) {
            val seekChar = s[seek]
            if (!hasHead) {
                if (canBeHead(seekChar)) {
                    // 如果本行中出现非避首字符（排除首字），那么设定有首字为true
                    hasHead = true
                }
                seek--
            } else {
                if (isEnglishChar(seekChar)) {
                    // 对于英数字符，向前查询到第一个非连续英数字符为止
                    val eseek = seek
                    var eng = true
                    while (eng && seek > 0) {
                        seek--
                        eng = isEnglishChar(s[seek])
                    }
                    // added by bdl, 2011.8.12, 如果从当前字符直到第一个都是连续的英文字符，
                    // 那么从最后一个英数字符前断行
                    if (seek == 0) {
                        loc = eseek + 1
                    }
                } else if (canBeFoot(seekChar)) {
                    // 如果不避尾，那么在该字符后断行即可
                    loc = seek + 1
                } else {
                    // 需要避尾
                    seek--
                }
            }
        }
        return if (loc > 0) {
            // 如果本行中可正常断行
            loc
        } else {
            // 如果本行中全都是避首或者是连续的避尾字符，那么正常切分
            len
        }
    } else if (isEnglishChar) {
        // 由于连续的英文字符需要视为一个单词共同换行，所以需要判断是否本行全部为连续英文字符
        var seek = len - 1
        var loc = 0
        var hasHead = canBeHead(c)
        var letterbreak = false
        while (seek >= 0 && loc == 0) {
            val seekChar = s[seek]
            if (!isEnglishChar(seekChar)) {
                // edited by bdl, 20111.5.17, 折行时首先判断当前首字符是否避首
                letterbreak = true
                if (!hasHead) {
                    if (canBeHead(seekChar)) {
                        hasHead = true
                    }
                    seek--
                } else if (canBeFoot(seekChar)) {
                    // 如果是非避尾字符，那么从这个字符后折行即可
                    loc = seek + 1
                } else {
                    hasHead = if (canBeHead(seekChar)) {
                        true
                    } else {
                        false
                    }
                    seek--
                }
            } else if (letterbreak) {
                // edited by bdl, 20111.5.17, 如果出现过非英文的避首字符
                // 如果出现避尾字符之后，又找到英文字符，那么从该英文字符后断开就好
                loc = seek + 1
            } else {
                // edited by bdl, 20111.5.17, 如果英文中出现避首字符
                hasHead = if (canBeHead(seekChar)) {
                    true
                } else {
                    false
                }
                seek--
            }
        }
        return if (loc > 0) {
            // 如果本行中有非英文字符
            loc
        } else {
            // 如果本行中全都是英文字符或者是连续的避尾字符，那么正常切分
            len
        }
    }
    return seekCanBeFoot(s.substring(0, len), len)
}

// added by bdl, 2008.5.21，将字符串s从最后面一个可置于本行行末的字符的后面断开，返回断开后本行字符数
private fun seekCanBeFoot(s: String, len: Int): Int {
    // 如果当前折行长度len已知，那就不去计算，否则计算当前折行时首行字符数
    var len = len
    if (len == -1) {
        len = s.length
    }
    if (len <= 1) {
        return len
    }

    var seek = len - 1
    var loc = 0
    while (seek >= 0 && loc == 0) {
        val seekChar = s[seek]
        if (canBeFoot(seekChar)) {
            loc = seek + 1
        } else {
            seek--
        }
    }
    if (loc > 0) {
        return loc
    }
    // 如果s中所有字符都是避尾字符，那么整行保留
    return len
}

// added by bdl, 2008.5.21，判断某字符通常情况下是否能作为行尾
private fun canBeFoot(c: Char): Boolean {
    // 断行的时候，应该考虑的避尾字符
    val cannotFoot = "([{·‘“〈《「『【〔〖（．［｛￡￥"
    return cannotFoot.indexOf(c) < 0
}

// added by bdl, 2008.5.21，判断某字符通常情况下是否能作为行首
private fun canBeHead(c: Char): Boolean {
    // 断行的时候，应该考虑的避首字符
    val cannotHead = "!),.:;?]}¨·ˇˉ―‖’”…∶、。〃々〉》」』】〕〗！＂＇），．：；？］｀｜｝～￠"
    return cannotHead.indexOf(c) < 0
}

private fun isEnglishChar(c: Char): Boolean {
    // return ( (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
    // (c >= '0' && c <= '9'));
    // edited by bd, 2018.4.4, 在一串字母数字构成的"超单词"中，word中是允许包含英文符号的
    // 这里的判断仿照这一规则处理，遇到空格、中文字符等才算做英文单词结束
    return (c in '!'..'~')
}

/**
 * Takes only the text before a comment
 *
 * @param allowEscapedQuotesInLiteralStrings value from TomlInputConfig
 * @return The text before a comment, i.e.
 * ```kotlin
 * "a = 0 # Comment".takeBeforeComment() == "a = 0"
 * ```
 */
fun String.takeBeforeComment(allowEscapedQuotesInLiteralStrings: Boolean): String {
    val commentStartIndex = getCommentStartIndex(allowEscapedQuotesInLiteralStrings)

    return if (commentStartIndex == -1) {
        this.trim()
    } else {
        this.substring(0, commentStartIndex).trim()
    }
}

/** Remove all consecutive line breaks in a list of `String` */
fun List<String>.removeDuplicateEmptyLines(): String =
    if (isNotEmpty()) {
        reduce { acc: String, b: String ->
            if (acc.lines().lastOrNull().isNullOrBlank() && b.isBlank()) acc else "$acc\n$b"
        }
    } else ""

fun List<String>.joinToCamelCaseString(): String {
    return joinToString(separator = "") { it.replaceFirstChar { it.uppercaseChar() } }
        .replaceFirstChar { it.lowercaseChar() }
}

val String.fitsOnOneLine: Boolean get() = !this.contains("\n")

fun String.multiline(indent: Int, prefix: String): String =
    this.split("\n").joinToString("\n") { elem -> indent.prefix + prefix + elem }

internal val Int.prefix: String get() = indentOf(this)

internal fun indentOf(num: Int): String = "  ".repeat(num)



/**
 * 按照\n切分为多行，并trim每一行（删除首尾的空格）。并且删除首尾的空行，保留中间的空行。 规范化注释文本。注释中可能包含因为
 *
 * @param comment 注释文本
 * @return 规范化后的注释文本
 */
fun normalizeComment(comment: String, trimChars: String): String {
    if (comment.isEmpty()) return comment

    val sb = StringBuilder()
    val parts = comment.split( '\n').toTypedArray()
    run {
        var i = 0
        val n = parts.size
        while (i < n) {
            var part = parts[i].trim { it <= ' ' }
            if (part.startsWith(trimChars)) {
                part = part.substring(trimChars.length)
            }
            parts[i] = part
            i++
        }
    }

    val start = 0
    var end = parts.size
    for (i in start..<end) {
        if (parts[i].isEmpty()) break
    }
    while (end > 0) {
        if (!parts[end - 1].isEmpty()) {
            break
        }
        end--
    }

    for (i in start..<end) {
        if (i != start) sb.append('\n')
        sb.append(parts[i])
    }
    return sb.toString()
}

/**
 * 在当前行查找目标字符串（直接向后查找，再检查是否跨行）
 *
 * @param input  输入字符串
 * @param start  起始搜索位置
 * @param target 要查找的目标字符串（如 "}}"）
 * @return 目标字符串的位置（如果找到且在同一行），否则返回 -1
 */
fun String.findInCurrentLine(target: String, start: Int): Int {
    val input = this
    if (start < 0 || start >= input.length) {
        return -1
    }

    // 1. 直接向后查找目标字符串
    val end = input.indexOf(target, start)
    if (end == -1) {
        return -1 // 没找到
    }

    // 2. 检查是否跨行（查找 start 到 end 之间是否有换行符）
    val nextNewline = input.indexOf('\n', start)
    if (nextNewline != -1 && end > nextNewline) {
        return -1 // 如果换行符在 start 和 end 之间，说明跨行
    }

    return end // 找到且在同一行
}